home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmbase-grok-1.2 / dbase.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  8KB  |  306 lines

  1. /*
  2.  * add and remove rows (cards) from the database
  3.  *
  4.  *    dbase_create()            create a new empty DBASE struct
  5.  *    dbase_delete(dbase)        delete contents of a DBASE struct
  6.  *    dbase_addrow(rp,dbase)        add a row to a database
  7.  *    dbase_delrow(r,dbase)        delete a row from a database
  8.  *    dbase_get(dbase,r,c)        get a string from the database
  9.  *    dbase_put(dbase,r,c,str)    put a string into the database
  10.  *    dbase_sort(dbase,c)        sort database by column c
  11.  */
  12.  
  13. #include "config.h"
  14. #include <X11/Xos.h>
  15. #include <ctype.h>
  16. #include <assert.h>
  17. #include <stdlib.h>
  18. #include <Xm/Xm.h>
  19. #include "grok.h"
  20. #include "form.h"
  21. #include "proto.h"
  22.  
  23. #define CHUNK    32            /* alloc 32 new rows at a time */
  24.  
  25. extern Widget    toplevel;        /* top-level shell for icon name */
  26. int        col_sorted_by;        /* dbase is sorted by this column */
  27.  
  28.  
  29.  
  30. /*
  31.  * allocate a new empty dbase struct with no data elements. Use dbase_addrow
  32.  * to add a row (card), and use free/mystrdup to fill individual data items
  33.  * in that row.
  34.  */
  35.  
  36. DBASE *dbase_create(void)
  37. {
  38.     DBASE        *dbase;        /* new dbase */
  39.  
  40.     dbase = (DBASE *)malloc(sizeof(DBASE));
  41.     mybzero((void *)dbase, sizeof(DBASE));
  42.     return(dbase);
  43. }
  44.  
  45.  
  46. /*
  47.  * destroy a dbase struct and all its data. The pointer passed is not freed.
  48.  */
  49.  
  50. void dbase_delete(
  51.     DBASE        *dbase)        /* dbase to delete */
  52. {
  53.     register int    r, c;        /* data counter */
  54.     register ROW    *row;        /* row to delete */
  55.  
  56.     if (!dbase)
  57.         return;
  58.     for (r=0; r < dbase->nsects; r++)
  59.         if (dbase->sect[r].path)
  60.             free(dbase->sect[r].path);
  61.     if (dbase->sect)
  62.         free(dbase->sect);
  63.  
  64.     for (r=0; r < dbase->nrows; r++) {
  65.         row = dbase->row[r];
  66.         for (c=row->ncolumns-1; c >= 0; c--)
  67.             if (row->data[c])
  68.                 free(row->data[c]);
  69.     }
  70.     if (dbase->row)
  71.         free(dbase->row);
  72.     mybzero((void *)dbase, sizeof(DBASE));
  73. }
  74.  
  75.  
  76. /*
  77.  * append a new row (card) to the database, and return the number of the
  78.  * appended row so the caller can store it in the card struct. Don't fill
  79.  * in any data yet, just add null pointers. Return FALSE if allocation
  80.  * failed. The caller must redraw the current card. Always put new cards
  81.  * into section dbase->currsect.
  82.  */
  83.  
  84. BOOL dbase_addrow(
  85.     int         *rowp,        /* ptr to returned row number */
  86.     register DBASE     *dbase)    /* database to add row to */
  87. {
  88.     register int     i, n;        /* size of data ptr array in bytes */
  89.     register ROW     *row;        /* new database row */
  90.     register SECTION *sect;        /* current insert section */
  91.  
  92.     if (dbase->nsects > 1 && dbase->currsect < 0)
  93.         return(FALSE);
  94.     if (dbase->nrows+1 >= dbase->size) {
  95.         i = (dbase->size + CHUNK) * sizeof(ROW);
  96.         if (!(dbase->row = (ROW **)(dbase->row ? realloc(dbase->row, i)
  97.                                : malloc(i))))
  98.             return(FALSE);
  99.         dbase->size += CHUNK;
  100.     }
  101.     n = dbase->maxcolumns ? dbase->maxcolumns : 1;
  102.     i = sizeof(ROW) + (n-1) * sizeof(char *);
  103.     if (i == 0) i = 1;
  104.     if (!(row = dbase->row[dbase->nrows] = malloc(i)))
  105.         return(FALSE);
  106.     sect = &dbase->sect[dbase->nsects > 1 ? dbase->currsect : 0];
  107.     mybzero(row, i);
  108.     row->ncolumns   = n;
  109.     row->section    = dbase->currsect > 0 ? dbase->currsect : 0;
  110.     dbase->modified = TRUE;
  111.     sect->modified  = TRUE;
  112.     sect->nrows++;
  113.     *rowp = dbase->nrows++;
  114.     print_info_line();
  115.     return(TRUE);
  116. }
  117.  
  118.  
  119. /*
  120.  * delete a row (card) in the database. Don't update any menus. This cannot
  121.  * fail. The caller must make sure to set card->row to a new value afterwards,
  122.  * and redraw the current card.
  123.  * <<< this messes up the query array in the card data structure.
  124.  */
  125.  
  126. void dbase_delrow(
  127.     int         nrow,        /* row to delete */
  128.     register DBASE     *dbase)    /* database to delete row in */
  129. {
  130.     register int     i;        /* copy counter */
  131.     register ROW     *row;        /* new database row */
  132.  
  133.     if (!dbase || nrow >= dbase->nrows)
  134.         return;
  135.     row = dbase->row[nrow];
  136.     dbase->sect[row->section].nrows--;
  137.     dbase->sect[row->section].modified = TRUE;
  138.     dbase->nrows--;
  139.     dbase->modified = TRUE;
  140.  
  141.     for (i=0; i < row->ncolumns; i++)
  142.         if (row->data[i])
  143.             free(row->data[i]);
  144.     free(row);
  145.     for (i=nrow; i < dbase->nrows; i++)
  146.         dbase->row[i] = dbase->row[i+1];
  147.     print_info_line();
  148. }
  149.  
  150.  
  151. /*
  152.  * retrieve a data string from the database, by row and column number. If
  153.  * the string does not exist for any reason, return 0.
  154.  */
  155.  
  156. char *dbase_get(
  157.     register DBASE    *dbase,        /* database to get string from */
  158.     register int    nrow,        /* row to get */
  159.     register int    ncolumn)    /* column to get */
  160. {
  161.     register ROW    *row;        /* row to get from */
  162.  
  163.     if (dbase && nrow >= 0
  164.           && nrow < dbase->nrows
  165.           && (row = dbase->row[nrow])
  166.           && ncolumn < row->ncolumns)
  167.         return(row->data[ncolumn]);
  168.     return(0);
  169. }
  170.  
  171.  
  172. /*
  173.  * store a string in the database. Add columns if necessary, but don't
  174.  * add rows at the end. It should never be necessary to add a row in
  175.  * the middle, this must have been done with dbase_addrow earlier. Don't
  176.  * write into read-only sections. Store empty strings as null pointers.
  177.  */
  178.  
  179. void dbase_put(
  180.     register DBASE    *dbase,        /* database to put into */
  181.     register int    nrow,        /* row to put into */
  182.     register int    ncolumn,    /* column to put into */
  183.     char        *data)        /* string to store */
  184. {
  185.     register ROW    *row;        /* row to put into */
  186.     register char    *p;
  187.  
  188.     if (!dbase || nrow < 0
  189.            || nrow >= dbase->nrows
  190.            || dbase->rdonly
  191.            || !(row = dbase->row[nrow])
  192.            || dbase->sect[row->section].rdonly)
  193.         return;
  194.     if (ncolumn >= dbase->maxcolumns)
  195.         dbase->maxcolumns = ncolumn + 1;
  196.     if (ncolumn >= row->ncolumns) {
  197.         if (!(row = realloc(row, sizeof(ROW) +
  198.                 (dbase->maxcolumns-1) * sizeof(char *))))
  199.             return;
  200.         mybzero(&row->data[row->ncolumns], sizeof(char *) *
  201.                     (dbase->maxcolumns - row->ncolumns));
  202.         dbase->row[nrow] = row;
  203.         row->ncolumns = dbase->maxcolumns;
  204.     }
  205.     if (data && !*data)
  206.         data = 0;
  207.     p = row->data[ncolumn];
  208.     if (!data && !p || data && p && !strcmp(data, row->data[ncolumn]))
  209.             return;
  210.     if (row->data[ncolumn])
  211.         free(row->data[ncolumn]);
  212.     row->data[ncolumn] = mystrdup(data);
  213.     dbase->modified = dbase->sect[row->section].modified = TRUE;
  214. }
  215.  
  216.  
  217. /*
  218.  * sort database by a column. Leading blanks are stripped before the
  219.  * comparison. If a string begins with a number, compare numerically.
  220.  * If not, or if the numbers are equal, compare lexicographically,
  221.  * case-insensitive. The caller must redraw the current card.
  222.  *
  223.  * Sorting is done on a copy of the real 2D data pointer array. The copy has
  224.  * one extra column that is non-null if the row is in the query array. This
  225.  * way, the cards selected of the last search or query can be re-selected
  226.  * after the sort, despite them having changed their card numbers.
  227.  */
  228.  
  229. static int primary_col;            /* primary sort criterium */
  230. static int secondary_col;        /* secondary sort criterium in case */
  231.                     /* primary_col column compares equal */
  232. static int compare_one(
  233.     MYCONST void    *u,
  234.     MYCONST void    *v,
  235.     register int    col)
  236. {
  237.     register ROW    *ru = *(ROW **)u;
  238.     register ROW    *rv = *(ROW **)v;
  239.     register char    *cu;
  240.     register char    *cv;
  241.     register double    du, dv;
  242.  
  243.     cu = col < ru->ncolumns ? ru->data[col] : 0;
  244.     cv = col < rv->ncolumns ? rv->data[col] : 0;
  245.     if (!cu || !cv)
  246.         return(!cv - !cu);
  247.     while (*cu == ' ' || *cu == '\t') cu++;
  248.     while (*cv == ' ' || *cv == '\t') cv++;
  249.     if ((isdigit(*cu) || *cu == '.') &&
  250.         (isdigit(*cv) || *cv == '.')) {
  251.         du = atof(cu);
  252.         dv = atof(cv);
  253.         if (du != dv)
  254.             return(du < dv ? -1 : du == dv ? 0 : 1);
  255.     }
  256.     return(mystrcasecmp(cu, cv));
  257. }
  258.  
  259.  
  260. static int compare(
  261.     register MYCONST void    *u,
  262.     register MYCONST void    *v)
  263. {
  264.     register int diff = compare_one(u, v, primary_col);
  265.     if (!diff && secondary_col != primary_col)
  266.         diff = compare_one(u, v, secondary_col);
  267.     return(diff);
  268. }
  269.  
  270.  
  271. void dbase_sort(
  272.     CARD        *card,        /* database and form to sort */
  273.     int        col)        /* column to sort by */
  274. {
  275.     register FORM    *form;        /* card form */
  276.     register DBASE    *dbase;        /* card data */
  277.     register ROW    **row;        /* list of row struct pointers */
  278.     register int    i, j;
  279.  
  280.     if (!card)
  281.         return;
  282.     dbase = card->dbase;
  283.     form  = card->form;
  284.     if (!dbase || !form || dbase->nrows < 2)
  285.         return;
  286.  
  287.     col_sorted_by = primary_col = secondary_col = col;
  288.     for (i=0; i < form->nitems; i++)
  289.         if (form->items[i]->defsort) {
  290.             secondary_col = form->items[i]->column;
  291.             break;
  292.         }
  293.     for (row=dbase->row, i=dbase->nrows; i; i--, row++)
  294.         (*row)->selected = FALSE;
  295.     for (i=card->nquery-1; i >= 0; i--)
  296.         dbase->row[card->query[i]]->selected = TRUE;
  297.  
  298.     qsort(card->dbase->row, dbase->nrows, sizeof(ROW *), compare);
  299.  
  300.     for (row=dbase->row, i=j=0; i < dbase->nrows; i++, row++)
  301.         if ((*row)->selected)
  302.             card->query[j++] = i;
  303.     card->qcurr = 0;
  304.     assert(j == card->nquery);
  305. }
  306.